package controller.preprocessor;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;

import javax.swing.Timer;

import model.cnf.CNF;
import model.preprocessor.IllegalPreProcessorParameterException;
import model.preprocessor.PreProcessorParameter;
import model.preprocessor.PreProcessorResult;

/**
 * Represent a preprocessor.
 * Override checkParameter() to put some limitation on the parameter that can be use with your preprocessor, by default it just call the {@link PreProcessorParameter} method to check parameter .
 * 
 * getCommandLine() return the list of parameter that will be use to run the preprocessor
 * runPreProcessor() is use to call the preprocessor
 * hasTimeOut() is to know if your preprocessor has some mechanism that allow it to stop itself after a certain time, if not this method should return false
 * 
 * evaluate() will evaluate the CNF using the method describe previously, it will get the parameter and run the preprocessor. If the preprocessor can't deal with the timeout, it will start a timer that will destroy the processus when the timeout is over.
 * 
 * Preprocessor that can't deal with timeout must set the attribute "running" at true when it is running, and use the attribute "process" to store the process executing the preprocessor in order to allow the internal timeout mechanism to work.   
 * 
 * @author stephane
 *
 */
public abstract class PreProcessor implements ActionListener{

	public static final int ADD_DELAY = 200; 
	
	protected String path = "";
	protected Timer timer = new Timer(PreProcessorParameter.DEFAULT_DELAY + ADD_DELAY, this);
	private boolean stop = false;
	private boolean running = false;

	/**
	 * store the process in this attribute
	 */
	protected Process process = null;
			
	/**
	 * if the preprocessor is running
	 * @return
	 */
	public boolean isRunning() {
		return running;
	}

	public void setRunning(boolean running) {
		this.running = running;
	}

	/**
	 * if the preprocessor has been stop
	 * @return
	 */
	public boolean hasBeenStop() {
		return stop;
	}
	
	/**
	 * return the path of the file associate to the preprocessor
	 * @return the path
	 */
	public String getPath() {
		return path;
	}
	
	/**
	 * return the preprocessor parameter
	 * @return the parameter
	 */
	public abstract PreProcessorParameter getPreProcessorParameter(); 
	
	/**
	 * determine if the preprocessor has a timeout mechanism that allow him to stop after a certain time
	 * @return if it has one
	 */
	public abstract boolean hasTimeout();
	
	/**
	 * check if the parameter are good 
	 * @return if they are allright
	 */
	public abstract boolean checkParameter();
	
	/**
	 * return the arguments to give the real preprocessor 
	 * @param cnfFile the cnf 
	 * @return a array of String equivalent to the command line you would write on a terminal
	 */
	public abstract String[] getCommandLine(String cnfFile);
	
	/**
	 * run the preprocessor with ce specified arguments
	 * @param cmd arguments
	 * @param fileOutput the file were the new cnf is gonna be stored
	 * @return the associate {@link PreProcessorResult}
	 */
	public abstract PreProcessorResult runPreProcessor(String[] cmd, String fileOutput);
	
	/**
	 * evaluate a CNF 
	 * @param cnf cnf to evaluate
	 * @param param parameter
	 * @return the result of the evaluation
	 * @throws IllegalPreProcessorParameterException 
	 */
	public PreProcessorResult evaluate(CNF cnf) throws IllegalPreProcessorParameterException{
		if(cnf == null )
			throw new IllegalArgumentException();
		if(!checkParameter())
			throw new IllegalPreProcessorParameterException();
		
		stop = running = false;
		process = null;
		
		//recupere un fichier avec la cnf
		File tempIn = cnf.getCNFfile();//cnf.export(directory);
		if(tempIn == null)
			return null;
			
		String[] cmdArray = getCommandLine(tempIn.getPath());

		if(getPreProcessorParameter().needTimeOut() && !hasTimeout())
		{//gere le timeout manuellement
			timer = new Timer(getPreProcessorParameter().getTimeOut() + ADD_DELAY, this);
			timer.start();
			System.out.println("demarre timer "+(getPreProcessorParameter().getTimeOut() + ADD_DELAY));
		}

		PreProcessorResult res = runPreProcessor(cmdArray, getPreProcessorParameter().getOut());
		
		//arrete timer
		timer.stop();
		//tempIn.delete();
		
		return res;
	}
	
	/**
	 * substitute the " " with "\ " for command argument in linux
	 * @param arg the original argument
	 * @return the argument with the "\ " instead of the " "
	 */
	public static String correctFormatForLinux(String arg){
		String res;
		if(arg.indexOf(' ') != -1)
			  res = arg.substring(0, arg.indexOf(' ')) + "\\ " + arg.substring(arg.indexOf(' ')+1);
		else res = arg;

		return res;
	}
	
	@Override
	public void actionPerformed(ActionEvent arg0) {
		
		if(arg0.getSource() == timer)
		{//doit terminer l'execution
			timer.stop();
			if(running && process != null)
			{//arrete 
				System.out.println("arrete le proc cause timeout");
				running =false;
				stop = true;
				process.destroy();
			}
		}
	}
	
	
}
